﻿using Cinchoo.Core;
using Cinchoo.Core.Net;
using Cinchoo.Core.Net.Sockets;
using Cinchoo.Core.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ChoWorkflowServer.Model
{
    public class ChoWSUDPConnectionManager : ChoNotifyPropertyChangedObject
    {
        public static readonly ChoWSUDPConnectionManager Instance = new ChoWSUDPConnectionManager();

        private IChoAbortableAsyncResult _server;
        private IChoAbortableAsyncResult _client;
        private AutoResetEvent _clientWait;
        private AutoResetEvent _controllerEnlistMsgEvent = null;
        private readonly ChoWSControllerEnlistMsg _serverControllerEnlistMsg = new ChoWSControllerEnlistMsg() { HostName = Environment.MachineName, HostIPAddress = ChoIPAddress.GetLocalIPAddress() };
        private ChoUDPMulticastClient _udpClient = null;

        private ChoWSControllerEnlistMsg _controllerEnlistMsg = null;
        public ChoWSControllerEnlistMsg ControllerEnlistMsg
        {
            get { return _controllerEnlistMsg; }
            set
            {
                if (_controllerEnlistMsg != value)
                {
                    _controllerEnlistMsg = value;
                    RaisePropertyChanged("ControllerEnlistMsg");
                }
            }
        }

        public event EventHandler<ChoEventArgs<KeyValuePair<string, string>[]>> ServerUnavailable;

        private void Reset()
        {
            if (_server != null)
            {
                _server.Abort();
                _server = null;
            }

            if (_clientWait != null)
            {
                _clientWait.Set();
                _clientWait = null;
            }

            if (_client != null)
            {
                _client.Abort();
                _client = null;
            }
        }

        public IChoAbortableAsyncResult StartAsServer()
        {
            ResetUDP();

            ChoWorkflowServerSettings workflowServerSettings = new ChoWorkflowServerSettings();
            ChoWFServerCmdLineArgsObject wfServerCmdLineArgsObject = new ChoWFServerCmdLineArgsObject();
            if (!workflowServerSettings.TurnOnUDP)
                return null;

            Console.WriteLine("Checking for any controller avail...");
            _controllerEnlistMsgEvent = new AutoResetEvent(false);
            wfServerCmdLineArgsObject.RunAsPeer = false;

            var x = workflowServerSettings.UDPIPAddress;
            //using (var waitFor = new ChoActionWaitFor(() =>
            //{
                _udpClient = new ChoUDPMulticastClient(workflowServerSettings.UDPIPAddress, workflowServerSettings.UDPPort, ChoNetMessageType.Binary);
                _udpClient.MessageReceived += MessageReceived;
                _udpClient.StartAsServer();
            //}))
            //{
            //    waitFor.Run();
            //}

            if (_controllerEnlistMsgEvent.WaitOne(10000)) //Wait for 10sec
            {
                wfServerCmdLineArgsObject.RunAsPeer = true;
                workflowServerSettings.ControllerIPAddress = ControllerEnlistMsg.HostIPAddress;
                Console.WriteLine("Controller found at {0}...".FormatString(workflowServerSettings.ControllerIPAddress));

                return null;
            }

            _server = ChoActionEx.RunAsync(() =>
            {
                Console.WriteLine("No Controller found, running this instance as controller...");

                while (true)
                {
                    _udpClient.Send(_serverControllerEnlistMsg);
                    Thread.Sleep(1000);
                }
            });

            return _server;
        }

        public void StartAsPeer()
        {
            ResetUDP();

            ChoWorkflowServerSettings workflowServerSettings = new ChoWorkflowServerSettings();
            ChoWFServerCmdLineArgsObject wfServerCmdLineArgsObject = new ChoWFServerCmdLineArgsObject();
            if (!workflowServerSettings.TurnOnUDP)
                return;

            Console.WriteLine("Checking for any controller avail...");
            _controllerEnlistMsgEvent = new AutoResetEvent(false);
            wfServerCmdLineArgsObject.RunAsPeer = false;

            var x = workflowServerSettings.UDPIPAddress;
            _udpClient = new ChoUDPMulticastClient(workflowServerSettings.UDPIPAddress, workflowServerSettings.UDPPort, ChoNetMessageType.Binary);
            _udpClient.MessageReceived += MessageReceived;

            if (_controllerEnlistMsgEvent.WaitOne(10000)) //Wait for 10sec
            {
                wfServerCmdLineArgsObject.RunAsPeer = true;
                workflowServerSettings.ControllerIPAddress = ControllerEnlistMsg.HostIPAddress;
                Console.WriteLine("Controller found at {0}...".FormatString(workflowServerSettings.ControllerIPAddress));
            }
        }

        public void StartAsClient()
        {
            ResetUDP();

            ChoWorkflowServerSettings workflowServerSettings = new ChoWorkflowServerSettings();
            ChoWFServerCmdLineArgsObject wfServerCmdLineArgsObject = new ChoWFServerCmdLineArgsObject();
            if (!workflowServerSettings.TurnOnUDP)
                return;

            Console.WriteLine("Checking for any controller avail...");
            _controllerEnlistMsgEvent = new AutoResetEvent(false);
            wfServerCmdLineArgsObject.RunAsPeer = true;

            var x = workflowServerSettings.UDPIPAddress;
            _udpClient = new ChoUDPMulticastClient(workflowServerSettings.UDPIPAddress, workflowServerSettings.UDPPort, ChoNetMessageType.Binary);
            _udpClient.MessageReceived += MessageReceived;

            if (_controllerEnlistMsgEvent.WaitOne(10000)) //Wait for 10sec
            {
                wfServerCmdLineArgsObject.RunAsPeer = true;
                workflowServerSettings.ControllerIPAddress = ControllerEnlistMsg.HostIPAddress;
                Console.WriteLine("Controller found at {0}...".FormatString(workflowServerSettings.ControllerIPAddress));
            }
        }

        private void ResetUDP()
        {
            if (_udpClient != null)
            {
                _udpClient.Dispose();
                _udpClient = null;
            }
        }

        private void MessageReceived(object sender, ChoNetMessageEventArgs e)
        {
            if (e.Message.Payload is ChoWSControllerEnlistMsg)
            {
                ControllerEnlistMsg = e.Message.Payload as ChoWSControllerEnlistMsg;
                _controllerEnlistMsgEvent.Set();
            }
        }
    }

    [Serializable]
    public class ChoWSControllerEnlistMsg : ChoEquatableObject<ChoWSControllerEnlistMsg>
    {
        public string HostName;
        public string HostIPAddress;
    }
}
